﻿using System;
using SimpleServiceBus.Bus;
using SimpleServiceBus.Bus.MessageManagement;
using SimpleServiceBus.Bus.MessageManagement.Callbacks;
using SimpleServiceBus.Bus.MessageManagement.MessageHandling;
using SimpleServiceBus.Bus.MessageManagement.Serialization;
using SimpleServiceBus.Bus.Pipeline;
using SimpleServiceBus.Bus.Pipeline.PipelineComponents.MessageFiltering;
using SimpleServiceBus.Bus.Pipeline.PipelineComponents.MessageLogging;
using SimpleServiceBus.Bus.Subscriptions;
using SimpleServiceBus.Bus.Transport;
using SimpleServiceBus.Endpoint;
using SimpleServiceBus.Endpoint.Management;
using SimpleServiceBus.Endpoint.Management.Monitoring;
using SimpleServiceBus.Endpoint.Management.Monitoring.Profiling;
using SimpleServiceBus.Endpoint.Metadata;
using SimpleServiceBus.Utilities.ExpressionEval;
using SimpleServiceBus.Utilities.ObjectBuilder;

namespace SimpleServiceBus.Configuration
{
    public class EndpointBuilder<TEndpointType, TRansportType> : IEndpointBuilder
        where TEndpointType : class, IMessagingEndpoint
        where TRansportType : class, ITransport
    {
        public TRansportType Transport
        {
            get { return (TRansportType) Endpoint.LocalServices.GetService<ITransport>(); }
        }

        #region IEndpointBuilder Members

        public IMessagingEndpoint Endpoint { get; private set; }
        public IObjectBuilderService Factory { get; private set; }

        public bool IsBuilt
        {
            get { return Endpoint != null; }
        }

        public IMessageTypeManagementService MessageTypes
        {
            get { return Endpoint.LocalServices.GetService<IMessageTypeManagementService>(); }
        }

        public IMessageHandlerContainer MessageHandlers
        {
            get { return Endpoint.LocalServices.GetService<IMessageHandlerResolutionService>(); }
        }

        public IMessageFilterService MessageFilters
        {
            get { return Endpoint.LocalServices.GetService<IMessageFilterService>(); }
        }

        public EndpointDescription Metadata
        {
            get
            {
                if (Endpoint == null)
                    return null;
                if (Endpoint.Metadata == null)
                    Endpoint.Metadata = new EndpointDescription();
                return Endpoint.Metadata;
            }
        }

        public void BuildEndpoint(IMessagingEndpoint endpoint)
        {
            if (endpoint == null)
                throw new ArgumentNullException("endpoint");

            Endpoint = endpoint;
            InitializeEndpoint();
        }

        public IMessagingEndpoint CreateEndpoint()
        {
            return CreateEndpoint(null);
        }

        public IMessagingEndpoint CreateEndpoint(IObjectBuilder ioc)
        {
            Factory = CreateObjectBuilderService(ioc);

            IMessagingEndpoint endpoint = Factory.BuildService<IMessagingEndpoint, TEndpointType>();

            endpoint.LocalServices.RegisterService(Factory);

            BuildEndpoint(endpoint);

            return Endpoint;
        }

        public IEndpointManagementClientService ConfigureEndpointManager(string monitorServiceAddress,
                                                                         TimeSpan statusReportInterval,
                                                                         TimeSpan statusReportGracePeriod,
                                                                         params IEndpointStatusProbe[] statusProbes)
        {
            IEndpointManagementClientService mgr =
                RegisterService<IEndpointManagementClientService, EndpointManagementClientService>();
            mgr.MonitorServiceAddress = monitorServiceAddress;
            if (statusReportInterval > TimeSpan.MinValue) mgr.StatusReportInterval = statusReportInterval;
            if (statusReportGracePeriod > TimeSpan.MinValue) mgr.StatusReportGracePeriod = statusReportGracePeriod;
            if (statusProbes != null && statusProbes.Length > 0)
            {
                mgr.EndpointStatusProbes.AddRange(statusProbes);
            }
            return mgr;
        }

        public IEndpointProfilerService ConfigureProfiler(bool enabled, bool profilePipelineComponents,
                                                          PipelineDirection pipelineDirection)
        {
            IEndpointProfilerService mgr = RegisterService<IEndpointProfilerService, EndpointProfilerService>();
            mgr.Enabled = enabled;
            mgr.ProfilePipelineComponents = profilePipelineComponents;
            mgr.PipelineDirection = pipelineDirection;
            return mgr;
        }

        public TServiceType GetService<TServiceType>()
            where TServiceType : class
        {
            return Endpoint.LocalServices.GetService<TServiceType>();
        }

        public TServiceType RegisterService<TServiceType, TDefaultImplementation>()
            where TServiceType : class
            where TDefaultImplementation : class, TServiceType
        {
            return RegisterService<TServiceType, TDefaultImplementation>(true);
        }

        public TServiceType RegisterService<TServiceType>(TServiceType serviceImplementation) where TServiceType : class
        {
            return RegisterService(serviceImplementation, true);
        }

        #endregion

        protected virtual void InitializeEndpoint()
        {
            EnsureFactory();

            //The message serializer should be started first, as the MessageBus
            //will be sending a ReadyMessage upon startup, and would
            //very much like to have a serializer ready and waiting to serialize that
            //message.
            RegisterService<IMessageSerializer, XmlMessageSerializer>(false);

            RegisterService<IMessageBus, MessageBus>(false);
            RegisterService<ITransport, TRansportType>(false);
            RegisterService<IMessageIdGenerator, DefaultMessageIdGenerator>(false);
            RegisterService<IMessagePipelineFactory, DefaultMessagePipelineFactory>(false);
            RegisterService<IMessageHandlerResolutionService, DefaultMessageHandlerResolutionService>(false);
            RegisterService<IMessageDispatcher, DefaultMessageDispatcher>(false);
            RegisterService<ICorrelatedCallbackManager, DefaultCallbackManager>(false);
            RegisterService<IMessageTypeManagementService, DefaultMessageTypeManagementService>(false);
            RegisterService<ISubscriptionRepository, XmlSubscriptionRepository>(false);
            RegisterService<ISubscriptionManager, DefaultSubscriptionManager>(false);
            RegisterService<IMessageLogger, DefaultMessageLogger>(false);
            RegisterService<IMessageExpressionEvaluator, MessageExpressionEvaluator>(false);
            RegisterService<IMessageFilterService, MessageFilterService>(false);
        }

        protected TServiceType RegisterService<TServiceType, TDefaultImplementation>(bool overrideExisting)
            where TServiceType : class
            where TDefaultImplementation : class, TServiceType
        {
            if (overrideExisting)
                return RegisterService<TServiceType>(Factory.BuildObject<TDefaultImplementation>());
            return RegisterService(Factory.BuildService<TServiceType, TDefaultImplementation>());
        }

        protected TServiceType RegisterService<TServiceType>(TServiceType serviceImplementation, bool overrideExisting)
            where TServiceType : class
        {
            if (overrideExisting || Endpoint.LocalServices.HasService<TServiceType>() == false)
            {
                if (serviceImplementation != null)
                    Endpoint.LocalServices.RegisterService(serviceImplementation);
                return serviceImplementation;
            }
            return Endpoint.LocalServices.GetService<TServiceType>();
        }

        protected virtual IObjectBuilderService CreateObjectBuilderService(IObjectBuilder ioc)
        {
            var svc = new DefaultObjectBuilderService(ioc);
            //RegisterService<IObjectBuilderService>(svc);
            return svc;
        }

        protected void EnsureFactory()
        {
            if (Factory == null)
            {
                Factory = CreateObjectBuilderService(null);
                if (Endpoint != null)
                    Endpoint.LocalServices.RegisterService(Factory);
            }
        }
    }
}